from typing import Callable, List, Optional, Tuple, Union
from inspect import Parameter, signature
from functools import wraps
from .decorator import decorator

from python_microservice_sdk.server import python_service_list
from python_microservice_sdk.http_handler import adapt_handler


def register(url: str, methods: List[str], python_only: bool, handler: Callable):
    """注册到Eureka服务器

    Args:
        url (str): 服务路由
        handler (Callable): 
    """
    # 未标明名称使用则使用方法名作为服务名
    if url is None:
        url = "/" + handler.__name__

    if not url.startswith("/"):
        url = "/" + url

    python_service_list.update(
        {url: adapt_handler(handler, methods, python_only)})


def assert_valid_service_provider(handler: Callable, methods: List[str]):
    """保证GET或DELETE方法的入参类型

    Args:
        handler (Callable): 方法处理器
        methods (List[str]): 可接受的方法名列表

    Raises:
        TypeError: 入参类型错误
    """
    sig = signature(handler)
    if "get" in methods or "delete" in methods:
        tip = "GET或DELETE方法必须保证入参为str, int, float, bool如果要传其他的类型，可以选择POST或PUT方法"
        for p_name, parameter in sig.parameters.items():
            if parameter.annotation is not None:
                if parameter.annotation not in [int, str, float, bool]:
                    raise TypeError("错误的参数类型 {name}: {t} {tip}".format(
                        name=p_name, t=parameter.annotation, tip=tip))

            if parameter.kind == Parameter.VAR_KEYWORD or parameter.kind == Parameter.VAR_POSITIONAL:
                raise TypeError("必须保证入参不含*args或**kwargs")


@decorator
def service(url: Optional[str] = None, methods: Optional[Union[str, List[str]]] = "get", python_only: bool = False) -> Tuple[Callable, Callable]:
    """装饰在微服务处理方法上的装饰器

    Args:
        url (str, optional): 微服务名称/路由. 默认为方法的名称.
        methods (str or List[str], optional): 微服务可接受的http方法. 默认为get.
        python_only (bool, optional): 是否只对python微服务客户端提供服务. 
                                      如果为True，微服务客户端和服务端将使用pickle持久化入参和返回值, 此时methods参数必须为post；
                                      如果False，使用json进行透传(json.dumps函数)，此时必须保证入参和返回值可以被json编码;
                                      默认为False，即兼容java和python的微服务.
    """

    def init_handler(handler: Callable):
        # 接受方法预处理
        _methods = methods
        if isinstance(methods, str):
            _methods = [methods.lower()]
        else:
            _methods = [m.lower() for m in _methods]

        # 验证方法合法性
        if python_only and methods != "post":
            raise TypeError("python_only=True时methods必须为post")
        assert_valid_service_provider(handler, methods)

        # 注册微服务方法
        register(url, _methods, python_only, handler)

    def invoke(handler: Callable, args: tuple, kwargs: dict):
        return handler(*args, **kwargs)

    return invoke, init_handler


@decorator
def python_service(url: Optional[str] = None):
    """装饰在微服务处理方法上的装饰器，仅对python客户端提供服务(使用pickle为持久层传输)

    Args:
        url (str, optional): 微服务名称/路由. 默认为方法的名称.

    Returns:
        service: 真正的装饰器
    """
    return service.__wrap_func__(url, "post", True)
